#include "TypeBuilder.h"

#include "ScriptCompiler/ScriptFileContext.h"

#include "ScriptCompiler/AST/TypeNode.h"
#include "ScriptCompiler/AST/BracketNode.h"

#include "ScriptCompiler/ASTBuilder/ExpressionBuilder.h"

CTypeBuilder::CTypeBuilder(std::shared_ptr<CScriptFileContext> InContext,bool bInResumeIfNotAType)
{
    Context = InContext;
    bResumeIfNotAType = bInResumeIfNotAType;
    Result = std::make_shared<CTypeNode>(InContext);
}

bool CTypeBuilder::ReadCurentToken()
{
    FirstTokenIndex = Context->CurrentTokenIndex;

    //read type name 
    {
        auto Token= Context->GetCurrentToken();
        if(Token->Type==EScriptTokenType::InnerType
            || Token->Type==EScriptTokenType::Var
        )
        {
            Result->NameChain.Add(Token);
            Context->CurrentTokenIndex++;
        }
        else if (Token->Type==EScriptTokenType::Identifier)
        {
            Result->NameChain.Add(Token);
            Context->CurrentTokenIndex++;
            
            while(true)
            {
                //read '.'
                auto DotToken= Context->GetCurrentToken();
                if(DotToken->Type==EScriptTokenType::Dot)
                {
                    Context->CurrentTokenIndex++; //skip '.'
                }
                else
                {
                    break;
                }

                //read next identifier
                auto NextToken= Context->GetCurrentToken();
                if(NextToken->Type==EScriptTokenType::Identifier)
                {
                    Result->NameChain.Add(NextToken);
                    Context->CurrentTokenIndex++;
                }
                else
                {
                    if(!bResumeIfNotAType)
                    {
                        Context->RaiseError(10128,NextToken,U"Expected identifier");
                    }
                    PostTypeTestFailed();
                    return false;
                }

            }



        }
        else
        {
            if(!bResumeIfNotAType)
            {
                Context->RaiseError(10129,Token,U"Expected type name");
            }
            else
            {
                PostTypeTestFailed();
            }
            return false;
        }
    }

    //read '<'
    {
        auto Token= Context->GetCurrentToken();
        if(Token->Type==EScriptTokenType::Less)
        {
            Context->CurrentTokenIndex++; //skip '<'

            //this is a generic type
            Result->GenericTypeLeftAngleBracket= Token;
            while(true)
            {
                auto TypeBuilder= std::make_shared<CTypeBuilder>(Context,bResumeIfNotAType);
                if(!TypeBuilder->ReadCurentToken())
                {
                    //this is not a type
                    if(bResumeIfNotAType)
                    {
                        PostTypeTestFailed();
                    }

                    return false;
                }
                Result->GenericTypes.Add(TypeBuilder->Result);

                Token= Context->GetCurrentToken();
                if(Token->Type==EScriptTokenType::Greater)
                {
                    Result->GenericTypeRightAngleBracket= Token;
                    Context->CurrentTokenIndex++; //skip '>'
                    break;
                }
                else if(Token->Type==EScriptTokenType::Comma)
                {
                    Context->CurrentTokenIndex++; //skip ','
                    continue;
                }
                else
                {
                    if(!bResumeIfNotAType)
                    {
                        Context->RaiseError(10130,Token,U"Expected '>' or ','");
                    }
                    return false;
                }
            }
        }

    }

    //read '('
    // {
    //     auto Token= Context->GetCurrentToken();
    //     if(Token->Type==EScriptTokenType::LeftParentheses)
    //     {
    //         Context->CurrentTokenIndex++; //skip '('

    //         //this is a function type
    //         while(true)
    //         {
    //             auto TypeBuilder= std::make_shared<CTypeBuilder>(Context,bResumeIfNotAType);
    //             if(!TypeBuilder->ReadCurentToken())
    //             {
    //                 //this is not a type
    //                 if(bResumeIfNotAType)
    //                 {
    //                     PostTypeTestFailed();
    //                 }

    //                 return false;
    //             }
    //             Result->FunctionParameters.Add(TypeBuilder->Result);

    //             Token= Context->GetCurrentToken();
    //             if(Token->Type==EScriptTokenType::RightParentheses)
    //             {
    //                 Context->CurrentTokenIndex++; //skip ')'
    //                 break;
    //             }
    //             else if(Token->Type==EScriptTokenType::Comma)
    //             {
    //                 Context->CurrentTokenIndex++; //skip ','
    //                 continue;
    //             }
    //             else
    //             {
    //                 if(!bResumeIfNotAType)
    //                 {
    //                     Context->RaiseError(10130,Token,U"Expected ')' or ','");
    //                 }
    //                 return false;
    //             }
    //         }
    //     }
    // }

    //read '['
    {
        auto Token= Context->GetCurrentToken();
        if(Token->Type==EScriptTokenType::LeftBracket)
        {
            Result->Bracket= std::make_shared<CBracketNode>(Context);
                                
            Context->CurrentTokenIndex++;  //skip  '['

            //find end ']'
            auto RightBracketIndex= FindPairedBracket(Context);
            if(!RightBracketIndex.HasValue())
            {
                Context->RaiseError(10131,Context->GetCurrentToken(),U"Expected paired ']' at end of array type");
                return false;
            }

            //read expression
            {
                auto ExpressionBuilder= std::make_shared<CExpressionBuilder>(Context,RightBracketIndex.GetValue());
                if(!ExpressionBuilder->ReadCurentToken())
                {
                    return false;
                }

                Result->Bracket.GetValue()->Expression= ExpressionBuilder->Result;
            }


            Context->CurrentTokenIndex++; //skip ']'
            
        }
    }


    return true;


}

int64_t CTypeBuilder::DetectType(std::shared_ptr<CScriptFileContext> InContext, int64_t StartTokenIndex)
{
    int64_t CurrentPosition = StartTokenIndex;

    while(true)
    {
        //read base type name 
        {
            auto Token = InContext->GetToken(CurrentPosition);
            if (Token->Type == EScriptTokenType::InnerType
                || Token->Type == EScriptTokenType::Var
                || Token->Type == EScriptTokenType::Identifier
                )
            {
                CurrentPosition++;
            }
            else
            {
                return -1;
            }
        }

        //skip "<...> if have
        {
            auto Token = InContext->GetToken(CurrentPosition);
            if (Token->Type == EScriptTokenType::Less)
            {
                CurrentPosition++; //skip '<'

                //find end '>'
                auto RightBracketIndex = FindPairedAngleBracket(CurrentPosition,InContext);
                if (!RightBracketIndex.HasValue())
                {
                    return -1;
                }

                CurrentPosition = RightBracketIndex.GetValue() + 1; //skip '>'
            }
        }

        //skip "[...]" if have
        {
            auto Token = InContext->GetToken(CurrentPosition);
            if (Token->Type == EScriptTokenType::LeftBracket)
            {
                CurrentPosition++; //skip '['

                //find end ']'
                auto RightBracketIndex = FindPairedBracket(CurrentPosition,InContext);
                if (!RightBracketIndex.HasValue())
                {
                    return -1;
                }

                CurrentPosition = RightBracketIndex.GetValue() + 1; //skip ']'
            }
        }

        //continue if have '.' or ->
        {
            auto Token = InContext->GetToken(CurrentPosition);
            if (Token->Type == EScriptTokenType::Dot
                || Token->Type == EScriptTokenType::Arrow
            )
            {
                CurrentPosition++; //skip '.'
                continue;
            }
        }

        break;
    }
    
    return CurrentPosition;
}

void CTypeBuilder::PostTypeTestFailed()
{
    if(bResumeIfNotAType)
    {
        Context->CurrentTokenIndex = FirstTokenIndex;
    }
}
